Given the continually increased reliance on computers in contemporary society, computer technology has had to advance on many fronts to keep up with increased demand. One particular subject of significant research and development efforts is parallelism, i.e., the performance of multiple tasks in parallel.
A number of computer software and hardware technologies have been developed to facilitate increased parallel processing. From a hardware standpoint, computers increasingly rely on multiple microprocessors to provide increased workload capacity. Furthermore, some microprocessors have been developed that support the ability to execute multiple threads in parallel, effectively providing many of the same performance gains attainable through the use of multiple microprocessors. From a software standpoint, multithreaded operating systems and kernels have been developed, which permit computer programs to concurrently execute in multiple threads so that multiple tasks can essentially be performed at the same time.
In addition, some computers implement the concept of logical partitioning, where a single physical computer is permitted to operate essentially like multiple and independent “virtual” computers (referred to as logical partitions), with the various resources in the physical computer (e.g., processors, memory, input/output devices) allocated among the various logical partitions. Each logical partition executes a separate operating system, and from the perspective of users and of the software applications executing on the logical partition, operates as a fully independent computer.
While parallelism effectively increases system performance by virtue of the ability to perform multiple tasks at once, one side effect of parallelism is an increased system complexity due to the need to synchronize the operation of multiple concurrent processes, particularly with regard to system resources that are capable of being shared by multiple threads. Separate threads or processes that are capable of accessing specific resources are typically not aware of the activities of other threads or processes. As such, a risk exists that one process might access a specific resource in an unexpected manner relative to another process, creating in indeterminate results and potential system errors.
As but one of many examples, an input/output (I/O) adapter such as an ethernet network adapter may have a finite number of resources that are capable of being shared by multiple threads or processes executing in a computer. One such type of resource is a frame buffer, which is used to store frames of data that are communicated to and received from a ethernet-compatible network. A frame buffer is often arranged into slots that are used in a sequential order by different processes, with the I/O adapter being notified of each slot that was consumed. Consuming one of the slots, however, may occupy a significant amount of time, during which other processes may not be able to utilize the other slots. Moreover, a concern exists that two processes may attempt to use the same slot at the same time.
To address these concerns, a serialization mechanism such as a lock or semaphore may be used to limit the access to a shared resource to one process at a time. A lock or semaphore is essentially a “token” that can be obtained exclusively by a process or thread in a multithreaded environment to access a particular shared entity or resource. Before a process or thread can access a resource, it must first obtain the token from the system. If another process or thread currently possesses the token, the former process or thread is not permitted to access the resource until the token is released by the other process or thread. In this manner, the accesses to the resource are effectively “serialized” to prevent indeterminate operations from occurring.
Whenever a shared resource is managed by a serialization mechanism, parallelism opportunities often diminish, as any thread or process that needs to access a locked resource often must wait until the lock on that resource is released before proceeding with accessing the resource. As such, there is a strong desire to minimize the frequency and duration of locks placed on shared resources.
With the aforementioned I/O adapter example, the most straightforward solution to ensuring conflict-free access to a frame buffer is to simply placing a lock around any use of the frame buffer. By doing so, however, parallelism opportunities are severely restricted, as a significant amount of work may need to be performed by a process to prepare, interpret and otherwise process the data stored in the frame buffer. Locking out access to the frame buffer by other processes while all of these activities occur is therefore counterproductive from a parallelism standpoint.
It may be possible in some circumstances to perform some up-front work associated with using a shared resource prior to obtaining a lock on the shared resource, thereby reducing the amount of time that the shared resource may need to be locked to other processes. However, conventional attempts directed in this manner have been found to require duplicate checking of resource availability and more complex up-front work to be performed.
A substantial need therefore exists for an improved manner of coordinating the access to a shared resource by multiple threads or processes executing in a multithreaded computer.